package com.jushu.storbox.entities;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

/**
 * @author aWX225227
 *
 * @param <Key> unique key word
 * @param <Container> container to store value
 * @param <Value> what to store
 */
public class AbsContainer<Key, Container, Value> {
	
	public final static int DEFAULT_ADD_INDEX = -1;
	
	private final HashMap<Key, Container> innerMap = new HashMap<Key, Container>();
	private final List<Container> innerList = new LinkedList<Container>();
	
	/**
	 * if you want to call this method, pleace implements 
	 * the relate method: </br>
	 * {@link AbsContainer#elementFilter(Key key) }</br>
	 * {@link AbsContainer#makeContainer(Key key) }</br>
	 * {@link AbsContainer#computContainerIndex(Key key) }</br>
	 * {@link AbsContainer#addValueToContainer(Key key, Container container, Value value) }
	 * {@link AbsContainer#addToContainerFailure(Key key) }</br>
	 * @param key the unique key container related
	 * @param value whitch to add </br>
	 * */
	public final boolean put(Key key, Value value) {
		if(key == null || value == null ) {
			return false;
		}
		
		if(elementFilter(key)) {
			return false;
		}
		
		Container container = get(key);
		if(container == null) {
			container = makeContainer(key);
			if(container != null) {
				int index = computContainerIndex(key);
				if(index >= 0 && index <= innerList.size()) {
					innerMap.put(key, container);
					innerList.add(index, container);
					addValueToContainer(key, container, value);
					return true;
				} else if(index == DEFAULT_ADD_INDEX) {
					innerMap.put(key, container);
					innerList.add(container);
					addValueToContainer(key, container, value);
					return true;
				} else {
					addToContainerFailure(key);
					return false;
				}
			} else {
				addToContainerFailure(key);
				return false;
			}
		} else {
			addValueToContainer(key, container, value);
			return true;
		}
	}
	
	/**
	 * @param key the unique key container related
	 * @return the container related by the key
	 */
	public final Container get(Key key) {
		return innerMap.get(key);
	}
	
	/**
	 * @param key  the unique key container related
	 */
	public final boolean remove(Key key) {
		Container obj = get(key);
		if(obj != null) {
			innerMap.remove(key);
			innerList.remove(obj);
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * if you call this method, pleace implements
	 * the related method:</br>
	 * {@link AbsContainer#replaceValueInContainer(Key key, Container container, Value value) }
	 * @param key
	 * @param value
	 */
	public final boolean replace(Key key, Value value) {
		if(value == null ) {
			return false;
		}
		Container container = get(key);
		if(container != null) {
			replaceValueInContainer(key, container, value);
			return true;
		} else {
			return false;
		}
	}
	
	public final void clear() {
		innerMap.clear();
		innerList.clear();
	}
	
	public final boolean isEmpty() {
		return innerMap.isEmpty();
	}
	
	public final HashMap<Key,Container> getMapedData() {
		return innerMap;
	}
	
	public final List<Container> getListedData() {
		return innerList;
	}

	/**
	 * {@link AbsContainer#put(Key key, Value value) }</br>
	 * @param key
	 * @return
	 */
	protected Container makeContainer(Key key) {
		return null;
	}
	
	/**
	 * 
	 * @param key
	 * @return the position to insert to
	 */
	protected int computContainerIndex(Key key) {
		return DEFAULT_ADD_INDEX;
	}
	
	/**
	 * relate method:</br>
	 * {@link AbsContainer#put(Key key, Value value) }</br>
	 * @param key witch to fileter
	 * @return filter(not add to container) if true, others false
	 */
	protected boolean elementFilter(Key key) {
		return false;
	}
	
	/**
	 * relate method:</br>
	 * {@link AbsContainer#put(Key key, Value value) }</br>
	 * {@link AbsContainer#makeContainer(Key key) }
	 * @param key
	 */
	protected void addToContainerFailure(Key key) { }
	
	/**
	 * relate method {@link AbsContainer#put(Key key, Value value) }
	 * @param key
	 * @param container
	 * @param value
	 */
	protected void addValueToContainer(Key key, Container container, Value value) { }
	
	/**
	 * relate method {@link AbsContainer#replace(Key key, Value value) }
	 * @param key
	 * @param container
	 * @param value
	 */
	protected void replaceValueInContainer(Key key, Container container, Value value) { }

}
